﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Net;
using System.Reflection;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;

namespace Docsbox.AccountEventReceiver
{
    ///<author>Steven Wu</author>
    ///<date>2012-12-17</date>
    ///<summary>Account List Item Event Receiver</summary>
    public class AccountEventReceiver : SPItemEventReceiver
    {
        private static string _amendmentEmailTemplate = "Docsbox.Resources.amendmentEmailTemplate.txt";
        private static string _amendPdfTemplate = "Docsbox.Resources.amendPdfTemplate.txt";

       /// <summary>
       /// An item is being added.
       /// </summary>
       public override void ItemAdding(SPItemEventProperties properties)
       {
           base.ItemAdding(properties);
           string title = Common.ConvertFieldToString(properties.AfterProperties[Common.FieldMTFNo], FieldType.Text);
           if(!IsValidRecord(title))
           {
               properties.Cancel = true;
               properties.Status = SPEventReceiverStatus.CancelWithError;
               properties.ErrorMessage = "This Deal Number format should be digits.";
           }
           SPList list = properties.List;
           if(CheckAccountExist(list, title))
           {
               properties.Cancel = true;
               properties.Status = SPEventReceiverStatus.CancelWithError;
               properties.ErrorMessage = "This Deal Number already exist in the system, please confirm the Deal Number and try again.";
           }
       }

        public bool IsValidRecord(string MTFNo)
       {
           if (Regex.IsMatch(MTFNo, @"^\d+$", RegexOptions.IgnoreCase))
               return true;
           return false;
       }
       /// <summary>
       /// An item is being updated.
       /// </summary>
       public override void ItemUpdating(SPItemEventProperties properties)
       {
           base.ItemUpdating(properties);
           
           
           var item = properties.ListItem;
           //check if this update change the status
           if(!item[Common.FieldAccountStatus].Equals(properties.AfterProperties[Common.FieldAccountStatus]))
           {
               string originalStatus = Common.ConvertLookupFieldToString(item[Common.FieldAccountStatus]);
               string newStatus = Common.GetLookupAccountStatusValue(properties.Web,Common.ConvertFieldToInt(properties.AfterProperties[Common.FieldAccountStatus]));
               bool isAmendmented = Convert.ToBoolean(item[Common.FieldIsAmendmented]);
               //if isAmendmented is true and status is changed to ready, set status to amendment ready instead
               if (isAmendmented && newStatus.Equals(Common.StatusReady) && !originalStatus.Equals(Common.StatusReady))
               {
                   //item[Common.FieldAccountStatus] = Common.GetLookupStatus(properties.OpenWeb(), Common.StatusAmendmentReady);
                   properties.AfterProperties[Common.FieldAccountStatus] = Common.GetLookupStatus(properties.OpenWeb(), Common.StatusAmendmentReady);
                   //this.EventFiringEnabled = false;
                   //item.Update();
                   //this.EventFiringEnabled = true;
               }
               //status changed to amendment required
               if(!originalStatus.Equals(Common.StatusAmendmentRequired) && newStatus.Equals(Common.StatusAmendmentRequired))
               {
                   //status changed from ready to amendment required, mark the isAmendmented to ture
                   if (originalStatus.Equals(Common.StatusReady) && newStatus.Equals(Common.StatusAmendmentRequired))
                   {
                       //item[Common.FieldIsAmendmented] = true;
                       properties.AfterProperties[Common.FieldIsAmendmented] = true;
                   }
                   //only auditor can mark status to amendment required, set auditor name
                   SPWeb web = properties.ListItem.Web;
                   SPUser auditor = web.CurrentUser;
                   string username = auditor.Name;
                   string email = auditor.Email;
                   
                   //SPFieldUserValue auditor = new SPFieldUserValue(web,properties.AfterProperties["Editor"].ToString());
                   //item[Common.FieldAuditor] = auditor;
                   properties.AfterProperties[Common.FieldAuditor] = Common.GetSpUserLookupValue(auditor);
                   //this.EventFiringEnabled = false;
                   //item.Update();
                   //this.EventFiringEnabled = true;
                   //send email to Project Owner (F&I Consultant) amendment summary PDF document
                   string documentUrl = GenerateEmailPdf(web,item,properties, item.ID,username,email);
                   //string amendmentEmailTemplateUrl = string.Format("{0}_layouts/VehicleFinance/amendmentEmailTemplate.txt", web.Url.EndsWith("/") ? web.Url : web.Url + "/");

                   string owner = Common.ConvertLookupFieldToString(item[Common.FieldAccountOwner]);
                   SPUser ownerAccount = web.EnsureUser(owner);

                   string amendmentEmailTemplate = GetFromResources(_amendmentEmailTemplate); //LoadTemplateContent(amendmentEmailTemplateUrl);
                   string subject = "Amendment Required - " + Common.ConvertFieldToString(item[Common.FieldMTFNo], FieldType.Text);
                   string emailContent = GetEmailContent(amendmentEmailTemplate, Common.ConvertFieldToString(item[Common.FieldMTFNo], FieldType.Text), username, documentUrl);
                   if(ownerAccount != null && ownerAccount.Email.Length > 0)
                        SendEmail(web, ownerAccount.Email, subject, emailContent);
               }
               if (newStatus.Equals(Common.StatusCompleted) && !originalStatus.Equals(Common.StatusCompleted))
               {
                   //audit complete, update the auditor 
                   SPWeb web = properties.ListItem.Web;
                   SPUser auditor = web.CurrentUser;
                   properties.AfterProperties[Common.FieldAuditor] = Common.GetSpUserLookupValue(auditor);
                   UpdateDocuments(web, item);
               }
           }
       }

       internal string GetFromResources(string resourceFileName)
       {
           Assembly assembly = Assembly.GetExecutingAssembly();
           try
           {
               using (Stream stream = assembly.GetManifestResourceStream(resourceFileName))
               {
                   using (var reader = new StreamReader(stream))
                   {
                       return reader.ReadToEnd();
                   }
               }
           }
           catch (Exception e)
           {
               return "Error: Loadding resource file error: " + resourceFileName;
           }
       }


       private string GetEmailContent(string amendmentEmailTemplate, string title, string auditor, string documentUrl)
        {
            return amendmentEmailTemplate.Replace(GetVariableName("Title"),title)
                .Replace(GetVariableName("Auditor"), auditor)
                .Replace(GetVariableName("AmendmentDocumentLinkUrl"), documentUrl);
        }

        private void SendEmail(SPWeb web,string to, string subject, string emailBody)
        {
            SPUtility.SendEmail(web, true, false, to, subject, emailBody);
        }

        private string GenerateEmailPdf(SPWeb web,SPListItem item, SPItemEventProperties properties,int accountId, string username, string email)
       {

           //1. generate PDF document
            //1.1 load html from _layouts/VehicleFinance/amendTemplate.html
            //1.2 set variables
            //1.3 pass variable to PDFHelper to generate PDF
           string webUrl = web.Url;
           if (!webUrl.EndsWith("/"))
               webUrl = webUrl + "/";
           //string templateUrl = webUrl + "_layouts/VehicleFinance/amendTemplate.html";

            //string template = LoadTemplateContent(templateUrl);

            string template = GetFromResources(_amendPdfTemplate);
           
            string pdfHtml = GetAmendmentHtml(template, item, properties, username, email);

            string dealNo = Common.ConvertFieldToString(item[Common.FieldMTFNo], FieldType.Text);
            PdfHelper helper = new PdfHelper();
            string fileName = string.Format("{0}-Amended{1}.PDF",
                                            dealNo,
                                            DateTime.Today.ToString("yyMMddHHmm"));
           string destinationUrl = string.Format("{0}{1}/{2}", webUrl, Common.ListDocuments,fileName );
           //2. save PDF to docsbox document library
                                                       
            byte[] pdfDoc = helper.CreatePdf(pdfHtml);

            SPFolder mtfDocument = web.GetFolder(Common.ListDocuments);
            SPFile file = mtfDocument.Files.Add(destinationUrl, pdfDoc);
            file.Update();

           //3. associate the PDF to this project
            SPListItem addedDocItem = file.Item;
            addedDocItem[Common.FieldAccountLookup] = accountId;

            addedDocItem.Update();

            //20121221-After PDF associated to an account, the document is moved to folder by hash table
            destinationUrl = UpdateDestinationUrlIntoMD5HashFolder(destinationUrl, dealNo);


            return destinationUrl;
           //4. Email the PDF to F&I Consultant

       }

        private string UpdateDestinationUrlIntoMD5HashFolder(string destinationUrl, string dealNo)
        {
            string folderName = dealNo.Substring(dealNo.Length - 2);
            string url = string.Format("{0}/{1}/{2}",
                                       destinationUrl.Substring(0, destinationUrl.LastIndexOf('/')), 
                                       folderName,                         
                                       destinationUrl.Substring(destinationUrl.LastIndexOf('/') + 1));
            return url;
        }

        private string LoadTemplateContent(string templateUrl)
        {
            string output = string.Empty;
            HttpWebRequest request = (HttpWebRequest) HttpWebRequest.Create(templateUrl);
            using(StreamReader reader = new StreamReader(request.GetResponse().GetResponseStream()))
            {
                output = reader.ReadToEnd();
            }
            return output;
        }

        private string GetVariableName(string fieldName)
        {
            return string.Format("[{0}]", fieldName);
        }
        
        private string GetAmendmentHtml(string template, SPListItem item, SPItemEventProperties properties, string username, string email)
        {
            return template.Replace(GetVariableName(Common.FieldMTFNo),
                                    Common.ConvertFieldToString(item[Common.FieldMTFNo], FieldType.Text))
                .Replace(GetVariableName(Common.FieldFinanceOffice),
                         Common.ConvertLookupFieldToString(item[Common.FieldFinanceOffice]))
                .Replace(GetVariableName(Common.FieldFirstName),
                         Common.ConvertFieldToString(item[Common.FieldFirstName], FieldType.Text))
                .Replace(GetVariableName(Common.FieldSurname),
                         Common.ConvertFieldToString(item[Common.FieldSurname], FieldType.Text))
                .Replace(GetVariableName(Common.FieldRegNo),
                         Common.ConvertFieldToString(item[Common.FieldRegNo], FieldType.Text))
                .Replace(GetVariableName(Common.FieldAccountStatus),
                         Common.ConvertLookupFieldToString(properties.AfterProperties[Common.FieldAccountStatus]))
                .Replace(GetVariableName(Common.FieldAccountComment),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldAccountComment], FieldType.Text))
                .Replace(GetVariableName(Common.FieldDriverLicenceApproved),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldDriverLicenceApproved], FieldType.Boolean))
                .Replace(GetVariableName(Common.FieldDriverLicenseComment),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldDriverLicenseComment], FieldType.Text))
                .Replace(GetVariableName(Common.FieldBankStatementApproved),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldBankStatementApproved], FieldType.Boolean))
                .Replace(GetVariableName(Common.FieldBankStatementComment),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldBankStatementComment], FieldType.Text))
                .Replace(GetVariableName(Common.FieldConfirmationOfAddressApproved),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldConfirmationOfAddressApproved], FieldType.Boolean))
                .Replace(GetVariableName(Common.FieldConfirmationOfAddressComment),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldConfirmationOfAddressComment], FieldType.Text))
                .Replace(GetVariableName(Common.FieldWagesSlipApproved),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldWagesSlipApproved], FieldType.Boolean))
                .Replace(GetVariableName(Common.FieldWagesSlipComment),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldWagesSlipComment], FieldType.Text))
                .Replace(GetVariableName(Common.FieldVisaResidencyApproved),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldVisaResidencyApproved], FieldType.Boolean))
                .Replace(GetVariableName(Common.FieldVisaResidencyComment),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldVisaResidencyComment], FieldType.Text))
                .Replace(GetVariableName(Common.FieldCreditCheckApproved),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldCreditCheckApproved], FieldType.Boolean))
                .Replace(GetVariableName(Common.FieldCreditCheckComment),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldCreditCheckComment], FieldType.Text))
                .Replace(GetVariableName(Common.FieldInsuranceApproved),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldInsuranceApproved], FieldType.Boolean))
                .Replace(GetVariableName(Common.FieldInsuranceComment),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldInsuranceComment], FieldType.Text))
                .Replace(GetVariableName(Common.FieldRPWApproved),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldRPWApproved], FieldType.Boolean))
                .Replace(GetVariableName(Common.FieldRPWComment),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldRPWComment], FieldType.Text))
                .Replace(GetVariableName(Common.FieldCDPCoverApproved),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldCDPCoverApproved], FieldType.Boolean))
                .Replace(GetVariableName(Common.FieldCDPCoverComment),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldCDPCoverComment], FieldType.Text))
                .Replace(GetVariableName(Common.FieldAffordabilityApproved),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldAffordabilityApproved], FieldType.Boolean))
                .Replace(GetVariableName(Common.FieldAffordabilityComment),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldAffordabilityComment], FieldType.Text))
                .Replace(GetVariableName(Common.FieldDepositRequirementApproved),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldDepositRequirementApproved], FieldType.Boolean))
                .Replace(GetVariableName(Common.FieldDepositRequirementComment),
                         Common.ConvertFieldToString(properties.AfterProperties[Common.FieldDepositRequirementComment], FieldType.Text))
                .Replace(GetVariableName(Common.FieldAuditor),username)
                .Replace(GetVariableName("AuditorEmail"), email);

        }


        /// <summary>
       /// An item is being deleted.
       /// </summary>
       public override void ItemDeleting(SPItemEventProperties properties)
       {
           base.ItemDeleting(properties);

           var item = properties.ListItem;
           string status = Common.ConvertLookupFieldToString(item[Common.FieldAccountStatus]);
           if (status.Equals(Common.StatusCompleted, StringComparison.CurrentCultureIgnoreCase))
           {
               properties.Cancel = true;
               properties.Status = SPEventReceiverStatus.CancelWithError;
               properties.ErrorMessage = "This completed account cannot be deleted.";
           }
       }

       /// <summary>
       /// An item was added.
       /// </summary>
       public override void ItemAdded(SPItemEventProperties properties)
       {
           base.ItemAdded(properties);

           var item = properties.ListItem;

           item[Common.FieldMTFNo] = Common.ConvertFieldToString(item[Common.FieldMTFNo], FieldType.Text).ToUpper();
           if (String.IsNullOrEmpty(Common.ConvertFieldToString(item[Common.FieldAccountStatus], FieldType.Text)))
                item[Common.FieldAccountStatus] = Common.GetLookupStatus(properties.OpenWeb(), Common.StatusLive);
           SPWeb web = properties.Web;
           SPUser owner = web.CurrentUser;
           item[Common.FieldAccountOwner] = Common.GetSpUserLookupValue(owner);


           this.EventFiringEnabled = false;
           item.Update();
           this.EventFiringEnabled = true;

           //move item to subfolder of its branch
           SPFile file = properties.List.ParentWeb.GetFile(item.Url);

           string office = Common.ConvertFieldToString(item[Common.FieldFinanceOffice], FieldType.Lookup);
           if(office.Length > 0)
           {
               string destinationUrl = file.Url.Replace(item.ID.ToString() + "_.000",
                                                             office + "/" + item.ID.ToString() + "_.000");

               this.EventFiringEnabled = false;
               file.MoveTo(destinationUrl, true);
               this.EventFiringEnabled = true;               
           }

       }

       /// <summary>
       /// An item was updated.
       /// </summary>
       public override void ItemUpdated(SPItemEventProperties properties)
       {
           base.ItemUpdated(properties);
           //SPWeb web = properties.Web;
           //var item = properties.ListItem;
           //string status = Common.ConvertLookupFieldToString(item[Common.FieldAccountStatus]);
           //if (status.Equals(Common.StatusCompleted, StringComparison.CurrentCultureIgnoreCase))
           //{
           //    UpdateDocuments(web,item);
           //}
       }

        private bool CheckAccountExist(SPList list, string title)
        {
            bool isExist = true;
            SPSecurity.RunWithElevatedPrivileges(delegate()
                                                     {
                                                         SPQuery query = new SPQuery();
                                                         query.ViewAttributes = "Scope=\"Recursive\"";
                                                         query.Query = GetAccountByDealNoQueryString(title);

                                                         SPListItemCollection collection = list.GetItems(query);
                                                         if (collection.Count == 0)
                                                             isExist = false;
                                                     });
            return isExist;
        }

       private void UpdateDocuments(SPWeb web, SPListItem item)
       {
           SPSecurity.RunWithElevatedPrivileges(delegate()
                                                    {
                string accountName = Common.ConvertFieldToString(item[Common.FieldMTFNo], FieldType.Text);
                //Combine all PDFs associated with this project to a single PDF
                //Delete All Associated PDFs
                //Save the combined PDF to docsbox document library
                SPList documents = web.Lists[Common.ListDocuments];
                SPQuery query = new SPQuery(documents.DefaultView);
                query.Query = GetDocumentsByAccountQueryString(accountName);
                query.QueryThrottleMode = SPQueryThrottleOption.Override;

                SPListItemCollection collection = documents.GetItems(query);

                //retrieve the IDs of the documents, for later on deletion
                List<int> documentIds = new List<int>();

                //get the Urls of the account associated PDF documents
                List<string> fileUrls = new List<string>();
                foreach (SPListItem listItem in collection)
                {

                    documentIds.Add(listItem.ID);
                    fileUrls.Add(web.Url + "/" + listItem.Url);
                }
                string username = ConfigurationManager.AppSettings["DocsboxDocumentUsername"];
                string password = ConfigurationManager.AppSettings["DocxboxDocumentPassword"];
                string domain = ConfigurationManager.AppSettings["DocsboxDocumentDomain"];
                //ICredentials documentReaderCredential = new NetworkCredential("swu", "Password3", "Auctions");
                ICredentials documentReaderCredential = new NetworkCredential(username, password, domain);
                PdfHelper helper = new PdfHelper(documentReaderCredential);

                byte[] result = helper.MergePdf(fileUrls.ToArray());

                //save the pdf to docsbox 


                string fileName = string.Format("{0}-Final.PDF",
                                                Common.ConvertFieldToString(item[Common.FieldMTFNo], FieldType.Text));
                string destinationUrl = string.Format("{0}{1}/{2}", web.Url, Common.ListDocuments, fileName);

                SPFolder mtfDocument = web.GetFolder(Common.ListDocuments);
                SPFile file = mtfDocument.Files.Add(destinationUrl, result);

                file.Update();

                //associate the PDF to this project
                SPListItem addedDocItem = file.Item;
                addedDocItem[Common.FieldAccountLookup] = item.ID;

                addedDocItem.Update();

                //delete all the documents from docsbox document library
                foreach (int id in documentIds)
                {
                    documents.Items.DeleteItemById(id);
                }

                                                    });
       }
       private string GetDocumentsByAccountQueryString(string filter)
       {
           String query = @"<Where><Eq><FieldRef Name='{1}' /><Value Type='Lookup'>{0}</Value></Eq><OrderBy><FieldRef Name='Created' Ascending='False' /></OrderBy></Where>";
           return string.Format(query, filter, Common.FieldAccountLookup);
       }
        private string GetAccountByDealNoQueryString(string title)
        {
            string query = @"<Where><Eq><FieldRef Name='Title' /><Value Type='Text'>{0}</Value></Eq></Where>";
            return string.Format(query, title);
        }

    }
}
